#include "board.h"

/* ---------------------------------------------*/
/* ------------ Static Functions -------------- */
/* ---------------------------------------------*/

/** @brief Initialize all the GPIO used by the board.*/
static void initGpio(void) {
    GPIO_InitTypeDef st_g;
    
    /* ----- Open clocks of GPIOs -----*/
    if ((PORT_MB_TX == GPIOA) || (PORT_MB_RX == GPIOA)) {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    }
    if ((PORT_MB_TX == GPIOB) || (PORT_MB_RX == GPIOB)) {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    }
    if ((PORT_MB_TX == GPIOC) || (PORT_MB_RX == GPIOC)) {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    }
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  // for pin TP1, TP2
    
    
    /* ----- Init USART GPIOs ----- */
    st_g.GPIO_Pin = PIN_MB_TX;
    st_g.GPIO_Mode = GPIO_Mode_AF_PP;
    st_g.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(PORT_MB_TX, &st_g);
    
    st_g.GPIO_Pin = PIN_MB_RX;
    st_g.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    st_g.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(PORT_MB_RX, &st_g);
    
    /* ----- Init Test Point ----- */
    st_g.GPIO_Pin = PIN_TP1 | PIN_TP2;
    st_g.GPIO_Mode = GPIO_Mode_Out_PP;
    st_g.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(PORT_TP1, &st_g);
    
    /* ----- Init LED ----- */
    st_g.GPIO_Pin = PIN_LED_0;
    st_g.GPIO_Mode = GPIO_Mode_Out_PP;
    st_g.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(PORT_LED_0, &st_g);
}


/* ---------------------------------------------*/
/* -------- Initialization Utilities ---------- */
/* ---------------------------------------------*/

/**
@brief Initialize USART peripheral for modbus 
@note Only config on baudrate is supported now.
*/
void initUsartMB(uint32_t baud) {
    USART_InitTypeDef st_u;
    NVIC_InitTypeDef _st_nvic;

    /* ---- Enable clock ----- */
    if (USART1 == USART_MB) {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    }
    else if (USART2 == USART_MB) {
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    }
    else if (USART3 == USART_MB) {
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
    }
    
    /* ----- Serial port settings ----- */
    st_u.USART_BaudRate = baud;
    st_u.USART_WordLength = USART_WordLength_8b;
    st_u.USART_StopBits = USART_StopBits_1;
    st_u.USART_Parity = USART_Parity_No;
    st_u.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    st_u.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART_MB, &st_u);
    
    /* ----- NVIC Config ----- */
    _st_nvic.NVIC_IRQChannel = USART_IRQN_MB;
    _st_nvic.NVIC_IRQChannelCmd = ENABLE;
    _st_nvic.NVIC_IRQChannelPreemptionPriority = 0;
    _st_nvic.NVIC_IRQChannelSubPriority = 0;
    
    NVIC_Init(&_st_nvic);
    
    /* ----- Enable USART ----- */
    USART_Cmd(USART_MB, ENABLE);
}


/** 
@brief Init timer with a period in micro seconds.
*/
//void initTimMB_us(uint32_t period_us) {
//    TIM_TimeBaseInitTypeDef st_tb;
//    RCC_ClocksTypeDef st_clk;
//    
//    /* ----- Enable Clock ----- */
//    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//    
//    /* ----- Get infomation about clock ----- */
//    RCC_GetClocksFreq(&st_clk);
//    
//    /* ----- Config Time Base ----- */
//    /* Clock tree of STM32F10x:
//        SYSCLK(max 72MHz) -> AHB PSC -> APB1 PSC (PCLK1) -> TIMxCLK (2,3,4,5)
//    */
//    if (period_us > 1) {
//        st_tb.TIM_Period = period_us - 1;
//    }
//    else {
//        st_tb.TIM_Period = 0;
//    }
//     
//    st_tb.TIM_Prescaler = st_clk.PCLK1_Frequency / 1000000ul -1;  // 1 us
//    st_tb.TIM_ClockDivision = TIM_CKD_DIV1;
//    st_tb.TIM_CounterMode = TIM_CounterMode_Up;
//    TIM_TimeBaseInit(TIM_MB, &st_tb);
//    
//    TIM_Cmd(TIM_MB, ENABLE);
//    
//    /* ----- Config NVIC ----- */
//    NVIC_InitTypeDef _st_nvic;
//    _st_nvic.NVIC_IRQChannel = TIM_MB_IRQN;
//    _st_nvic.NVIC_IRQChannelCmd = ENABLE;
//    _st_nvic.NVIC_IRQChannelPreemptionPriority = 0;
//    _st_nvic.NVIC_IRQChannelSubPriority = 1;
//    NVIC_Init(&_st_nvic);
//}


/** 
@brief Init timer with a period in micro seconds.
*/
void initSysTick_us(uint32_t period_us) {
    RCC_ClocksTypeDef st_clk;
    uint32_t ticks;
    
    /* ----- Config SysTick ----- */
    RCC_GetClocksFreq(&st_clk);
    ticks = st_clk.HCLK_Frequency / 1000000UL * period_us;
        
    if (SysTick_Config(ticks)) {
        // Capture error
        while (1) ;
    }
    
    enableSysTick();
    
    /* ----- Config NVIC ----- */
//    NVIC_InitTypeDef _st_nvic;
//    _st_nvic.NVIC_IRQChannel = SysTick_IRQn;
//    _st_nvic.NVIC_IRQChannelCmd = ENABLE;
//    _st_nvic.NVIC_IRQChannelPreemptionPriority = 0;
//    _st_nvic.NVIC_IRQChannelSubPriority = 1;
//    NVIC_Init(&_st_nvic);
}


void enableSysTick(void) {
    SysTick->VAL = SysTick->LOAD;  // reset to reload value
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
}

void disableSysTick(void) {
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}


void board_EnableAllClocks(void) {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
//    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
//    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
}


void board_ConfigAllClocks(void) {
    RCC_PCLK2Config(RCC_HCLK_Div1);
    
}


void initBoard(void) {
    // ===== Hardware =====
    initGpio();
    initSysTick_us(10);  // tick in period of 10us
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);  // 0 bit for pre-emption
    
    // ===== Software =====
    SfTim_InitSys();
}

